package com.base.springboot.starter.swagger2.service;

import com.base.common.enums.CommonEnum;
import com.base.common.enums.SwaggerDisplayEnum;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import springfox.documentation.builders.ModelPropertyBuilder;
import springfox.documentation.schema.Annotations;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 对swagger展示进行修改 ，支持将枚举变量的描述按照枚举类定义展示
 *
 * @see SwaggerDisplayEnum
 * @see ApiModelProperty
 *
 * @author admin
 * @date 2020/5/14 AM10:59
 */
@Component
@Primary
@Slf4j
public class SwaggerDisplayConfig implements ModelPropertyBuilderPlugin {
    /**
     * 是否允许swagger
     */
    @Value("${swagger.enable:true}")
    private Boolean enableSwagger;
    private BeanPropertyDefinition beanPropertyDefinition;

    public SwaggerDisplayConfig() {
        log.info("SwaggerDisplayConfig init...");
    }

    @Override
    public void apply(ModelPropertyContext context) {
        //如果不支持swagger的话，直接返回
        if (!enableSwagger) {
            return;
        }
        // 获取当前字段的类型
        beanPropertyDefinition = context.getBeanPropertyDefinition().get();
        final Class<?> fieldType = beanPropertyDefinition.getField().getRawType();

        // 为枚举字段设置注释
        descForEnumFields(context, fieldType);
    }

    /**
     * 为枚举字段设置注释
     */
    private void descForEnumFields(ModelPropertyContext context, Class<?> fieldType) {
        Optional<ApiModelProperty> annotation = Optional.empty();

        if (context.getAnnotatedElement().isPresent()) {
            annotation = Optional.ofNullable(annotation.orElseGet(ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get())::get));
        }
        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = Optional.ofNullable(annotation.orElseGet(Annotations.findPropertyAnnotation(context.getBeanPropertyDefinition().get(),
                    ApiModelProperty.class)::get));
        }

        // 没有@ApiModelProperty 或者 notes 属性没有值，直接返回
        if (!annotation.isPresent() || StringUtils.isEmpty((annotation.get()).notes())) {
            return;
        }

        // @ApiModelProperties中的notes指定的class类型
        Class<?> rawPrimaryType;
        try {
            rawPrimaryType = Class.forName((annotation.get()).notes());
        } catch (ClassNotFoundException e) {
            // 如果指定的类型无法转化，直接忽略
            return;
        }

        // 如果对应的class是一个@SwaggerDisplayEnum修饰的枚举类，获取其中的枚举值
        Object[] subItemRecords = null;
        SwaggerDisplayEnum swaggerDisplayEnum = AnnotationUtils.findAnnotation(rawPrimaryType,
                SwaggerDisplayEnum.class);
        if (null != swaggerDisplayEnum && Enum.class.isAssignableFrom(rawPrimaryType)) {
            subItemRecords = rawPrimaryType.getEnumConstants();
        }
        if (null == subItemRecords) {
            return;
        }

        // 从annotation中获取enum对应的value和描述
        String[] valueNames = swaggerDisplayEnum.valueNames().split(";");
        String[] descNames = swaggerDisplayEnum.descNames().split(";");
        if (valueNames.length < 1 || descNames.length < 1) {
            return;
        }

//		循环枚举值
        final List<String> displayValues = Arrays.stream(subItemRecords).filter(Objects::nonNull).map(item -> {
            Class<?> currentClass = item.getClass();

            String value ;
            String desc = "";
            try {
                Field valueField = null;
//				循环enum-valueNames，找到枚举当前循环的value和descp
                for (int i = 0; i < valueNames.length; i++) {
                    if (item.toString().equalsIgnoreCase(valueNames[i])) {
                        valueField = currentClass.getField(valueNames[i]);
                        desc = descNames[i];
                    }
                }
                if(valueField!=null){
                    valueField.setAccessible(true);
                }
                CommonEnum commonEnum = (CommonEnum)item;
                value = String.valueOf(commonEnum.getCode());
            } catch (NoSuchFieldException e) {
                log.error("获取枚举的属性和值失败, {0}", e);
                return null;
            }
            return value + ":" + desc;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        String joinText = " (" + String.join("; ", displayValues) + ")";
        try {
            Field mField = ModelPropertyBuilder.class.getDeclaredField("description");
            mField.setAccessible(true);
            joinText = mField.get(context.getBuilder()) + joinText;
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        final ResolvedType resolvedType = context.getResolver().resolve(fieldType);
        context.getBuilder().description(joinText).type(resolvedType);
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }
}
